import {AxiosPromise, AxiosRequestConfigs, AxiosResponse, Method,RejectedFn,ResolvedFn} from "@/types";
import dispatchRequest from "@/core/dispatchRequest";
import InterceptorManager from "@/core/InterceptorManager";

interface Interceptors {
    request: InterceptorManager<AxiosRequestConfigs>,
    response: InterceptorManager<AxiosResponse>
}

interface PromiseChain<T> {
    resolved:ResolvedFn<T> | ((config:AxiosRequestConfigs)=>AxiosPromise),
    rejected?:RejectedFn
}


export default class Axios {
    interceptors: Interceptors

    constructor() {
        this.interceptors = {
            request: new InterceptorManager<AxiosRequestConfigs>(),
            response: new InterceptorManager<AxiosResponse>()
        }
    }

    request(url: any, config?: any): AxiosPromise {
        if (typeof url === 'string') {
            if (!config) {
                config = {}
            }
            config.url = url;
        } else {
            //此时代表url是个config对象,将<AxiosRequestConfigs>url
            config = url;
        }
        const chain:PromiseChain<any>[] = [{
            resolved: dispatchRequest,
            rejected: undefined
        }]

        //请求拦截器先添加的后执行
        this.interceptors.request.forEach(interceptor => {
            chain.unshift(interceptor)
        })

        //响应拦截器先添加的先执行
        this.interceptors.response.forEach(interceptor => {
            chain.push(interceptor)
        })

        let promise = Promise.resolve(config)
        while(chain.length){
            //chcin可能是promiseChain可能为空数组 断言不为空
            const { resolved,rejected} = chain.shift()!;
            promise = promise.then(resolved,rejected)
        }

        // return dispatchRequest(config)
        return <AxiosPromise<any>>promise
    }

    get(url: string, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('get', url, config)
    }

    delete(url: string, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('delete', url, config)
    }

    head(url: string, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('head', url, config)
    }

    options(url: string, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('options', url, config)
    }

    post(url: string, data?: any, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('post', url, config)
    }

    put(url: string, data?: any, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('post', url, config)
    }

    patch(url: string, data?: any, config?: AxiosRequestConfigs): AxiosPromise {
        return this._requestMethodWithData('post', url, config)
    }

    _requestMethodWithData(method: Method, url: string, data?: any, config?: AxiosRequestConfigs): AxiosPromise {
        return this.request(Object.assign(config || {}, {
            method,
            url,
            data
        }))
    }
}
